package com.example.teesystem.service.impl;

import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.example.teesystem.common.exception.CustomException;
import com.example.teesystem.common.utils.IssueUtil;
import com.example.teesystem.common.utils.Response;
import com.example.teesystem.common.utils.ResultCode;
import com.example.teesystem.entity.*;
import com.example.teesystem.entity.vo.exam.ExamUpdateVo;
import com.example.teesystem.entity.vo.examEvaluation.ExamEvaluationInsertVo;
import com.example.teesystem.entity.vo.examIssue.ExamIssueDeleteVo;
import com.example.teesystem.entity.vo.examIssue.ExamIssueInsertVo;
import com.example.teesystem.entity.vo.examIssue.ExamIssueUpdateVo;
import com.example.teesystem.entity.vo.examIssue.IssueScoreVo;
import com.example.teesystem.entity.vo.examPaper.ExamPaperInstead;
import com.example.teesystem.entityDto.NotMarkIssueMessage;
import com.example.teesystem.mapper.*;
import com.example.teesystem.service.ExamIssueService;
import com.example.teesystem.service.ExamPaperService;
import com.example.teesystem.service.ExamService;
import com.example.teesystem.service.IssueRecordService;
import lombok.RequiredArgsConstructor;
import org.springframework.beans.BeanUtils;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Propagation;
import org.springframework.transaction.annotation.Transactional;

import java.util.*;
import java.util.stream.Collectors;

/**
 * @author NieYaShi
 * @description 针对表【t_exam_issue】的数据库操作Service实现
 * @createDate 2022-04-11 19:18:53
 */
@Service
@RequiredArgsConstructor
public class ExamIssueServiceImpl extends ServiceImpl<ExamIssueMapper, ExamIssue>
        implements ExamIssueService {

    private final ExamMapper examMapper;
    private final ExamIssueMapper examIssueMapper;
    private final ExamPaperMapper examPaperMapper;
    private final IssueMapper issueMapper;
    private final WrongSetMapper wrongSetMapper;

    private final ExamPaperService examPaperService;
    private final ExamService examService;
    private final IssueRecordService issueRecordService;

    private static final int ERROR = -1;

    @Override
    @Transactional(propagation = Propagation.REQUIRED, rollbackFor = Exception.class)
    public Response addExamIssue(ExamIssueInsertVo examIssueInsertVo) {
        ExamPaper check = examPaperMapper.selectById(examIssueInsertVo.getPaperId());
        if (check == null) {
            return Response.error(ResultCode.EXAM_EXIST_ERROR);
        }
        if (check.getIsPublish() == 1) {
            return Response.error(ResultCode.EXAM_PAPER_IS_PUBLISHED);
        }
        if (check.getIsFinish() == 1) {
            return Response.error(ResultCode.EXAM_PAPER_IS_FINISHED);
        }
        if (check.getIsMark() == 1) {
            return Response.error(ResultCode.EXAM_PAPER_IS_MARKED);
        }

        if (!examIssueInsertVo.getIssueId().trim().equals("")) {
            String[] ids = examIssueInsertVo.getIssueId().split("\\$");
            List<Integer> issueIds = examIssueMapper.getIssueListByPaperId(examIssueInsertVo.getPaperId());
            if (issueIds.size() != 0){
                for (String id : ids) {
                    if (!issueIds.contains(Integer.parseInt(id))) {
                        ExamIssue examIssue = new ExamIssue();
                        examIssue.setPaperId(examIssueInsertVo.getPaperId());
                        examIssue.setIssueId(Integer.parseInt(id));
                        int insertId = ERROR;
                        examIssueMapper.insert(examIssue);
                        insertId = examIssue.getId();
                        if (insertId == ERROR) {
                            return Response.error(ResultCode.INSERT_ERROR);
                        }
                        issueMapper.addReferenceNum(Integer.parseInt(id));
                    }
                }
            }else {
                for (String id : ids) {
                    ExamIssue examIssue = new ExamIssue();
                    examIssue.setPaperId(examIssueInsertVo.getPaperId());
                    examIssue.setIssueId(Integer.parseInt(id));
                    int insertId = ERROR;
                    examIssueMapper.insert(examIssue);
                    insertId = examIssue.getId();
                    if (insertId == ERROR) {
                        return Response.error(ResultCode.INSERT_ERROR);
                    }
                    issueMapper.addReferenceNum(Integer.parseInt(id));
                }
            }

            // 更新试卷包含题目类型
            updateExamPaperIncludeIssueType(examIssueInsertVo.getPaperId());

            return Response.ok();
        } else {
            return Response.error(ResultCode.PARAMETER_MISS.getMsg(), ResultCode.PARAMETER_MISS.getCode());
        }
    }

    @Override
    @Transactional(propagation = Propagation.REQUIRED, rollbackFor = Exception.class)
    public Response updateExamIssueScore(ExamIssueUpdateVo examIssueUpdateVo) {
        ExamPaper check = examPaperMapper.selectById(examIssueUpdateVo.getPaperId());

        if (check == null) {
            return Response.error(ResultCode.EXAM_EXIST_ERROR);
        }
        if (check.getIsPublish() == 1) {
            return Response.error(ResultCode.EXAM_PAPER_IS_PUBLISHED);
        }
        if (check.getIsFinish() == 1) {
            return Response.error(ResultCode.EXAM_PAPER_IS_FINISHED);
        }
        if (check.getIsMark() == 1) {
            return Response.error(ResultCode.EXAM_PAPER_IS_MARKED);
        }

        List<Integer> issueIds = examIssueMapper.getIssueListByPaperId(examIssueUpdateVo.getPaperId());
        if (issueIds.size() == 0){
            return Response.error(ResultCode.EXAM_NOT_HAVE_ISSUE_ERROR);
        }
        List<IssueScoreVo> issueScoreVoList = examIssueUpdateVo.getIssueScoreVoList();
        List<Integer> uploadIssueIds = new ArrayList<>();
        for (IssueScoreVo issueScoreVo : issueScoreVoList){
            uploadIssueIds.add(issueScoreVo.getIssueId());
        }

        if (uploadIssueIds.size() != issueIds.size()){
            return Response.error(ResultCode.EXAM_ISSUE_NOT_COMPLETE_ERROR);
        }
        Map<String, List<Integer>> diff = getDifferent(issueIds, uploadIssueIds);
        if (diff.size() != 0) {
            return Response.error(ResultCode.UPDATE_MESSAGE_ERROR, diff);
        } else {
            for (IssueScoreVo issueScoreVo : issueScoreVoList) {
                try {
                    examIssueMapper.updateExamIssueScore(issueScoreVo.getIssueId(), issueScoreVo.getScore(), examIssueUpdateVo.getPaperId());
                } catch (Exception e) {
                    Response.error(ResultCode.UPDATE_MESSAGE_ERROR);
                }
            }
            return Response.ok();
        }
    }

    @Override
    @Transactional(propagation = Propagation.REQUIRED, rollbackFor = Exception.class)
    public Response deleteExamIssues(ExamIssueDeleteVo examIssueDeleteVo) {
        ExamPaper examPaper = examPaperMapper.selectById(examIssueDeleteVo.getPaperId());

        if (examPaper == null) {
            return Response.error(ResultCode.EXAM_EXIST_ERROR);
        }
        if (examPaper.getIsPublish() == 1) {
            return Response.error(ResultCode.EXAM_PAPER_IS_PUBLISHED);
        }
        if (examPaper.getIsFinish() == 1) {
            return Response.error(ResultCode.EXAM_PAPER_IS_FINISHED);
        }
        if (examPaper.getIsMark() == 1) {
            return Response.error(ResultCode.EXAM_PAPER_IS_MARKED);
        }

        List<Integer> examIssueList = examIssueMapper.getIssueListByPaperId(examIssueDeleteVo.getPaperId());

        if (examIssueList.size() < examIssueDeleteVo.getIssueIds().length ){
            return Response.error(ResultCode.EXAM_ISSUE_NOT_EXIST_ERROR);
        }

        for (Integer issueId : examIssueDeleteVo.getIssueIds()) {
            if (examIssueList.contains(issueId)) {
                examIssueMapper.deleteExamIssue(examIssueDeleteVo.getPaperId(), issueId);
                issueMapper.subReferenceNum(issueId);
            }
        }
        // 更新试卷包含题目类型
        updateExamPaperIncludeIssueType(examIssueDeleteVo.getPaperId());

        return Response.ok();
    }

    private void updateExamPaperIncludeIssueType(int paperId){
        List<Integer> issueIds = examIssueMapper.getIssueListByPaperId(paperId);
        if (issueIds.size() == 0){
            examPaperMapper.updateIncludeIssueType(paperId, "");
        }else {

            QueryWrapper<Issue> issueQueryWrapper = new QueryWrapper<>();
            issueQueryWrapper.in("id", issueIds);
            List<Issue> issueList = issueMapper.selectList(issueQueryWrapper);

            Map<Integer, List<Issue>> map = issueList.stream().collect(Collectors.groupingBy(
                    Issue::getType));

            StringBuffer stringBuffer = new StringBuffer();
            for (Integer key : map.keySet()) {
                stringBuffer.append(key.toString());
                stringBuffer.append("$");
            }

            stringBuffer.delete(stringBuffer.length() - 1, stringBuffer.length());

            examPaperMapper.updateIncludeIssueType(paperId, stringBuffer.toString());
        }
    }

    private Map<String, List<Integer>> getDifferent(List<Integer> list1, List<Integer> list2) {
        Map<Integer, Integer> map = new HashMap<>();
        Map<String, List<Integer>> resultMap = new HashMap<>();
        List<Integer> diff = new ArrayList<>();
        List<Integer> maxList = list1;
        List<Integer> minList = list2;
        if (list2.size() > list1.size()) {
            maxList = list2;
            minList = list1;
        }

        for (Integer string : maxList) {
            map.put(string, 1);
        }

        List<Integer> errorList = new ArrayList<>();
        for (Integer string : minList) {
            Integer cc = map.get(string);
            if (cc != null) {
                map.put(string, ++cc);
                continue;
            }
            errorList.add(string);
        }

        for (Map.Entry<Integer, Integer> entry : map.entrySet()) {
            if (entry.getValue() == 1) {
                diff.add(entry.getKey());
            }
        }

        if (diff.size() != 0) {
            resultMap.put("errorIssueIds", errorList);
            resultMap.put("lostIssueIds", diff);
        }
        return resultMap;
    }

    @Override
    public Response getNotMarkIssue(Integer paperId, Integer studentId) {
        List<NotMarkIssueMessage> notMarkIssueMessages = new ArrayList<>();

        ExamPaper check = examPaperMapper.selectById(paperId);

        if (check == null) {
            return Response.error(ResultCode.EXAM_EXIST_ERROR);
        }
        if (check.getIsPublish() == 0) {
            return Response.error(ResultCode.EXAM_NOT_PUBLISH_ERROR);
        }
        if (check.getIsFinish() == 0) {
            return Response.error(ResultCode.EXAM_NOT_FINISH_ERROR);
        }
        if (check.getIsMark() == 1) {
            return Response.error(ResultCode.EXAM_PAPER_IS_MARKED);
        }

        QueryWrapper<ExamIssue> examIssueQueryWrapper = new QueryWrapper<>();
        examIssueQueryWrapper
                .eq("paper_id", paperId)
                .orderByAsc("issue_id");
        List<ExamIssue> examIssueList = examIssueMapper.selectList(examIssueQueryWrapper);
        if (examIssueList.size() == 0){
            return Response.ok(notMarkIssueMessages);
        }

        QueryWrapper<Exam> examQueryWrapper = new QueryWrapper<>();
        examQueryWrapper
                .eq("paper_id", paperId)
                .eq("student_id", studentId)
                .orderByAsc("issue_id");
        List<Exam> examList = examMapper.selectList(examQueryWrapper);
        if (examList.size() == 0){
            examPaperService.finishMarkSingleStudentPaper(new ExamEvaluationInsertVo(paperId, studentId, ""));
            return Response.ok(notMarkIssueMessages);
        }
        List<Integer> ids = new ArrayList<>();
        for (Exam exam : examList) {
            ids.add(exam.getIssueId());
        }

        QueryWrapper<Issue> issueQueryWrapper = new QueryWrapper<>();
        issueQueryWrapper
                .in("id", ids)
                .orderByAsc("id");
        List<Issue> issueList = issueMapper.selectList(issueQueryWrapper);

        autoMarkingExamPaper(examIssueList, examList, issueList, studentId);

        for (int i = 0; i < examList.size(); i++) {
            if (examList.get(i).getIsMark() == 0 && (issueList.get(i).getType() == IssueUtil.FILL_IN_THE_BLANKS || issueList.get(i).getType() == IssueUtil.BRIEF_ANSWER)) {
                notMarkIssueMessages.add(new NotMarkIssueMessage(examList.get(i), examIssueList.get(i), issueList.get(i)));
            }
        }

        examList = examMapper.selectList(examQueryWrapper);
        if (examList.size() == 0||examList==null){
            examPaperService.finishMarkSingleStudentPaper(new ExamEvaluationInsertVo(paperId, studentId, ""));
        }

        return Response.ok(notMarkIssueMessages);

    }

    @Transactional(propagation = Propagation.REQUIRED, rollbackFor = Exception.class)
    public void autoMarkingExamPaper(List<ExamIssue> examIssueList, List<Exam> examList, List<Issue> issueList, Integer studentId) {
        for (int i = 0; i < examList.size(); i++) {
            if (issueList.get(i).getType() != IssueUtil.BRIEF_ANSWER && issueList.get(i).getType() != IssueUtil.FILL_IN_THE_BLANKS) {
                String studentAnswer = examList.get(i).getAnswer();
                if (studentAnswer == null){
                    studentAnswer = "";
                }
                String realAnswer = issueList.get(i).getAnswer();
                Exam exam = new Exam();
                BeanUtils.copyProperties(examList.get(i), exam);
                if (examService.checkAnswer(realAnswer, studentAnswer)) {
                    issueRecordService.addIssueRecord(examIssueList.get(i).getIssueId(),"",true);
                    exam.setScore(examIssueList.get(i).getScore());
                } else {
                    issueRecordService.addIssueRecord(examIssueList.get(i).getIssueId(),"",false);
                    WrongSet wrongSet = new WrongSet();
                    wrongSet.setIssueId(issueList.get(i).getId());
                    wrongSet.setStudentId(studentId);

                    WrongSet def = wrongSetMapper.getWrongSetByStuIdAndIssueId(wrongSet.getStudentId(), wrongSet.getIssueId());
                    if (def == null) {
                        int id = ERROR;
                        wrongSetMapper.insert(wrongSet);
                        id = wrongSet.getId();
                        if (id == ERROR) {
                            throw new CustomException(ResultCode.INSERT_ERROR);
                        }
                    }
                }
                exam.setIsMark(1);
                examMapper.updateById(exam);
            }
        }
    }

    @Override
    public Response updateExamScore(ExamUpdateVo examUpdateVo) {
        List<Exam> examList = examMapper.getExamsByPaperIdAndStudentId(examUpdateVo.getPaperId(), examUpdateVo.getStudentId());
        if (examList.size() == 0){
            return Response.error(ResultCode.STUDENT_NOT_ANSWER_ERROR);
        }

        List<Integer> ids = new ArrayList<>();
        for (Exam exam : examList) {
            ids.add(exam.getIssueId());
        }
        List<Issue> issueList = issueMapper.getIssuesByIds(ids);

        List<Integer> notMarkIds = new ArrayList<>();
        for (Issue issue : issueList){
            if (issue.getType() == IssueUtil.FILL_IN_THE_BLANKS || issue.getType() == IssueUtil.BRIEF_ANSWER){
                notMarkIds.add(issue.getId());
            }
        }

        if (!notMarkIds.contains(examUpdateVo.getIssueId())) {
            return Response.error(ResultCode.EXAM_ISSUE_NOT_EXIST_ERROR);
        }

        Exam check = examList.stream().filter(exam -> exam.getIssueId().equals(examUpdateVo.getIssueId())).collect(Collectors.toList()).get(0);
        check.setScore(examUpdateVo.getScore());
        check.setMessage(examUpdateVo.getMessage());
        check.setIsMark(1);

        try {
            examMapper.updateById(check);
            List<Exam> examsNotMark = examMapper.check(examUpdateVo.getPaperId(),examUpdateVo.getStudentId());
            if(examsNotMark == null||examsNotMark.size() ==0 ){
                examPaperService.finishMarkSingleStudentPaper(new ExamEvaluationInsertVo(examUpdateVo.getPaperId(),examUpdateVo.getStudentId(),""));
            }
        }catch (Exception e){
            return Response.error(ResultCode.UPDATE_INFO_FAIL);
        }

        return Response.ok();
    }

    @Override
    @Transactional(propagation = Propagation.REQUIRED, rollbackFor = Exception.class)
    public Response insteadIssueScore(ExamPaperInstead examPaperInstead) {
        try{
            ExamPaper examPaper = examPaperMapper.selectById(examPaperInstead.getPaperId());
            Map<Integer,Integer> map = new HashMap<>();
            int score = 0;
            if(examPaper == null) {
                return Response.error(ResultCode.EXAM_EXIST_ERROR);
            }
            examIssueMapper.deleteByPaperId(examPaperInstead.getPaperId());
            for(int i = 0; i < examPaperInstead.getIssueList().size(); i++){
                if(map.containsKey(examPaperInstead.getIssueList().get(i).getIssueId())) {
                    continue;
                }
                ExamIssue examIssue = new ExamIssue();
                examIssue.setIssueId(examPaperInstead.getIssueList().get(i).getIssueId());
                examIssue.setPaperId(examPaperInstead.getPaperId());
                examIssue.setScore(examPaperInstead.getIssueList().get(i).getScore());
                examIssueMapper.insert(examIssue);
                score = score + examPaperInstead.getIssueList().get(i).getScore();
                map.put(examPaperInstead.getIssueList().get(i).getIssueId(),1);
            }
            examPaper.setScore(score);
            examPaperMapper.updateById(examPaper);
            return Response.ok();
        }catch (Exception e){
            return Response.error(ResultCode.UPDATE_MESSAGE_ERROR);
        }
    }

}




